package com.yanxp.mybatis.session;

import com.yanxp.mybatis.config.BoundSql;
import com.yanxp.mybatis.config.Configuration;
import com.yanxp.mybatis.config.MapperMetadata;
import com.yanxp.mybatis.utils.GenericTokenParser;
import com.yanxp.mybatis.utils.ParameterMapping;
import com.yanxp.mybatis.utils.ParameterMappingTokenHandler;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Sqlsession 会话信息
 *
 * @author yuanxp
 * @Date 2021/3/4 15:56
 */
public class SqlSessionImpl implements SqlSession {

    private Configuration configuration;

    public SqlSessionImpl(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <T> List<T> selectList(String namespace, String id, Object... params) {
        List<T> userList = new ArrayList<T>();
        PreparedStatement preparedStatement = null;
        Connection connection = null;
        ResultSet resultSet = null;
        try {
            connection = configuration.getDataSource().getConnection();
            MapperMetadata mapperMetadata = configuration.getMapper(namespace).get(id);
            String sql = mapperMetadata.getSql();
            //解析sql，将#{} 替换成 ?
            BoundSql boundSql = getBoundSql(sql);
            preparedStatement = connection.prepareStatement(boundSql.getSqlText());
            String paramterType = mapperMetadata.getParamterType();
            if (paramterType != null && paramterType.trim().length() > 0) {
                Class<?> paramtertypeClass = Class.forName(paramterType);
                //设置参数
                List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
                for (int i = 0; i < parameterMappingList.size(); i++) {
                    ParameterMapping parameterMapping = parameterMappingList.get(i);
                    String content = parameterMapping.getContent();

                    //反射
                    Field declaredField = paramtertypeClass.getDeclaredField(content);
                    //暴力访问
                    declaredField.setAccessible(true);
                    Object o = declaredField.get(params[0]);

                    preparedStatement.setObject(i + 1, o);

                }
            }

            //执行SQL，返回结果集
            resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //封装结果集的列名
            List<String> columnNames = new ArrayList<>();
            for (int i = 1; i <= columnCount; i++) {
                columnNames.add(metaData.getColumnName(i));
            }

            String resultType = mapperMetadata.getResultType();
            Class clazz = Class.forName(resultType);
            Field[] fields = clazz.getDeclaredFields();
            while (resultSet.next()) {
                Object o = clazz.newInstance();
                for (Field field : fields) {
                    field.setAccessible(true);
                    for (String columnName : columnNames) {
                        if (columnName.equals(field.getName())) {
                            field.set(o, resultSet.getObject(columnName));
                        }
                    }
                }

                userList.add((T) o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) resultSet.close();
                if (preparedStatement != null) preparedStatement.close();
                if (connection != null) connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return userList;
    }

    @Override
    public <T> T selectOne(String namespace, String id, Object... params) {
        List<T> result = selectList(namespace, id, params);
        return result.size() > 0 ? result.get(0) : null;
    }


    /**
     * 完成对#{}的解析工作：1.将#{}使用？进行代替，2.解析出#{}里面的值进行存储
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类：配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        //#{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
        return boundSql;

    }
}
